import queue
from datetime import datetime
import json
import os
import time
import uuid
from queue import Queue
from threading import Timer

import requests
from flask import session
from flask_login import current_user

from app import db

from app.iot_mqtt import mqtt_client
from app.iot_mqtt.command import command
from app.iot_mqtt.iot_config import device_properties, device_config, log_messages, HUAWEI_IOT_CONFIG
from collections import deque

from app.models import Device, DeviceShadowData, User


def log(msg):
    """记录日志"""
    timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
    log_entry = f"{timestamp} - {msg}"
    print(log_entry)
    log_messages.append(log_entry)
    # 保持日志不超过100条
    if len(log_messages) > 100:
        log_messages.pop(0)


# MQTT回调函数
def on_connect(client, userdata, flags, rc):
    """MQTT连接成功回调"""
    if rc == 0:
        log(f"连接到MQTT服务器成功，设备ID: {device_config['username']}")
        subscribe_topics()
        show_device_info()
        start_shadow_query_interval()
    else:
        log(f"连接失败，错误代码: {rc}")


def on_message(client, userdata, msg):
    """接收到MQTT消息回调"""
    topic = msg.topic
    payload = msg.payload.decode()

    log(f"收到消息，Topic: {topic}")
    log(f"消息内容: {payload}")
    print("message:", payload)
    try:
        # 处理设备影子查询响应
        if "/sys/shadow/get/response/request_id=" in topic:
            request_id = topic.split("request_id=")[1]
            if payload:
                shadow_data = json.loads(payload)
                handle_device_shadow_response(request_id, shadow_data)
            else:
                handle_device_shadow_response(request_id, {})
            return

        msg_data = json.loads(payload)

        # 处理命令请求
        if "/sys/commands/request_id=" in topic:
            request_id = topic.split("request_id=")[1]
            handle_command(request_id, msg_data)

        # 处理属性设置请求
        elif "/sys/properties/set/request_id=" in topic:
            request_id = topic.split("request_id=")[1]
            handle_property_set(request_id, msg_data)

        # 处理属性查询请求
        elif "/sys/properties/get/request_id=" in topic:
            request_id = topic.split("request_id=")[1]
            handle_property_get(request_id)

    except Exception as e:
        log(f"消息处理错误: {str(e)}")


def on_disconnect(client, userdata, rc):
    """MQTT断开连接回调"""
    log("MQTT服务器连接已关闭")
    if rc != 0:
        log(f"意外断开连接，错误代码: {rc}")


# MQTT相关功能
def subscribe_topics():
    """订阅相关Topic"""
    # 订阅命令下发Topic
    topic = get_command_topic()
    mqtt_client.subscribe(topic)
    log(f"订阅命令Topic成功: {topic}")

    # 订阅属性设置Topic
    topic = get_property_set_topic()
    mqtt_client.subscribe(topic)
    log(f"订阅属性设置Topic成功: {topic}")

    # 订阅属性查询Topic
    topic = get_property_get_topic()
    mqtt_client.subscribe(topic)
    log(f"订阅属性查询Topic成功: {topic}")


# 命令上报 调节


def report_device_properties():
    """上报设备属性"""
    global is_local_led_change

    if not is_local_led_change:
        log("非本地LED状态变化，不上报数据")
        return

    topic = get_property_report_topic()

    # 只上报LED状态
    led_only_properties = {
        "services": [
            {
                "service_id": "mqtt",
                "properties": {
                    "led": device_properties["services"][0]["properties"]["led"],
                    "hum": 23,
                    "tem": 15
                },
                "event_time": None
            }
        ]
    }
    payload = json.dumps(led_only_properties)

    is_local_led_change = False
    log("本地LED状态变化，只上报LED状态")

    mqtt_client.publish(topic, payload)
    log(f"属性上报成功，Topic: {topic}")
    log(f"上报内容: {payload}")


def handle_command(request_id, command):
    """处理命令请求"""
    log(f"收到命令请求，ID: {request_id}")
    log(f"命令内容: {json.dumps(command)}")

    global is_local_led_change

    # 处理LED控制命令
    if command.get("command_name") == "set_led":
        if "paras" in command and "value" in command["paras"]:
            is_local_led_change = False
            value = command["paras"]["value"]
            device_properties["services"][0]["properties"]["led"] = value == "ON" or value is True
            log(f"LED状态已更新为: {device_properties['services'][0]['properties']['led']}（云端下发）")

    # 发送命令响应
    response_topic = get_command_response_topic(request_id)
    response = {
        "result_code": 0,
        "response_name": "COMMAND_RESPONSE",
        "paras": {
            "result": "success"
        }
    }
    payload = json.dumps(response)

    mqtt_client.publish(response_topic, payload)
    log(f"命令响应已发送，Topic: {response_topic}")
    log(f"响应内容: {payload}")


def handle_property_set(request_id, property_set):
    """处理属性设置请求"""
    log(f"收到属性设置请求，ID: {request_id}")
    log(f"设置内容: {json.dumps(property_set)}")

    global is_local_led_change

    if "services" in property_set and property_set["services"]:
        for service in property_set["services"]:
            if service.get("service_id") == "mqtt" and "properties" in service:
                for prop, value in service["properties"].items():
                    if prop in device_properties["services"][0]["properties"]:
                        # 如果是LED属性，标记为非本地变化
                        if prop == "led":
                            is_local_led_change = False
                            log("LED属性由云端设置，不会触发上报")
                        device_properties["services"][0]["properties"][prop] = value
                        log(f"属性 {prop} 已更新为: {value}")

    # 发送属性设置响应
    response_topic = get_property_set_response_topic(request_id)
    response = {
        "result_code": 0,
        "result_desc": "success"
    }
    payload = json.dumps(response)

    mqtt_client.publish(response_topic, payload)
    log(f"属性设置响应已发送，Topic: {response_topic}")


def handle_property_get(request_id):
    """处理属性查询请求"""
    log(f"收到属性查询请求，ID: {request_id}")

    # 发送属性查询响应
    response_topic = get_property_get_response_topic(request_id)
    payload = json.dumps(device_properties)

    mqtt_client.publish(response_topic, payload)
    log(f"属性查询响应已发送，Topic: {response_topic}")
    log(f"响应内容: {payload}")


def query_device_shadow():
    """查询设备影子数据"""
    request_id = str(int(time.time() * 1000))
    topic = get_device_shadow_query_topic(request_id)

    log(f"发送设备影子查询请求，请求ID: {request_id}")

    # 发送空消息体查询设备影子
    mqtt_client.publish(topic, "")
    log(f"设备影子查询请求已发送，Topic: {topic}")

    # 订阅响应Topic
    response_topic = get_device_shadow_query_response_topic(request_id)
    mqtt_client.subscribe(response_topic)
    log(f"订阅设备影子响应Topic成功: {response_topic}")


def handle_device_shadow_response(request_id, shadow_data):
    """处理设备影子查询响应"""
    log(f"收到设备影子查询响应，请求ID: {request_id}")
    log(f"设备影子数据: {json.dumps(shadow_data)}")

    global is_local_led_change

    # 更新本地设备属性
    if shadow_data and "shadow" in shadow_data and shadow_data["shadow"]:
        shadow = shadow_data["shadow"][0]
        if "reported" in shadow and "properties" in shadow["reported"]:
            reported_props = shadow["reported"]["properties"]

            for prop, value in reported_props.items():
                if prop in device_properties["services"][0]["properties"]:
                    # 标记为云端下发
                    if prop == "led":
                        is_local_led_change = False
                    device_properties["services"][0]["properties"][prop] = value
                    log(f"从设备影子更新属性 {prop} 为: {value}")

            # 显示更新后的设备信息
            show_device_info()

    log("设备影子处理完成")


# Topic生成函数
def get_property_report_topic():
    return f"$oc/devices/{device_config['username']}/sys/properties/report"


def get_command_topic():
    return f"$oc/devices/{device_config['username']}/sys/commands/+"


def get_command_response_topic1():
    return f"$oc/devices/{device_config['username']}/sys/commands/+"


def get_command_response_topic(request_id):
    return f"$oc/devices/{device_config['username']}/sys/commands/response/request_id={request_id}"


def get_property_set_topic():
    return f"$oc/devices/{device_config['username']}/sys/properties/set/+"


def get_property_set_response_topic(request_id):
    return f"$oc/devices/{device_config['username']}/sys/properties/set/response/request_id={request_id}"


def get_property_get_topic():
    return f"$oc/devices/{device_config['username']}/sys/properties/get/+"


def get_property_get_response_topic(request_id):
    return f"$oc/devices/{device_config['username']}/sys/properties/get/response/request_id={request_id}"


def get_device_shadow_query_topic(request_id):
    return f"$oc/devices/{device_config['username']}/sys/shadow/get/request_id={request_id}"


def get_device_shadow_query_response_topic(request_id):
    return f"$oc/devices/{device_config['username']}/sys/shadow/get/response/request_id={request_id}"


# 设备控制函数


data_queue = deque()
ai_queue = deque(maxlen=300)  # 假设一秒钟一条数据集 最多300条数据

# 调用API的方法
# 全局变量存储 token（替代 session）
_huawei_token = None
_token_expire = 0


def get_huawei_token():
    global _huawei_token, _token_expire

    auth_data = {
        "auth": {
            "identity": {
                "methods": ["password"],
                "password": {
                    "user": {
                        "name": HUAWEI_IOT_CONFIG['USERNAME'],
                        "password": HUAWEI_IOT_CONFIG['PASSWORD'],
                        "domain": {"name": HUAWEI_IOT_CONFIG['DOMAIN']}
                    }
                }
            },
            "scope": {
                "project": {"name": HUAWEI_IOT_CONFIG['PROJECT']}
            }
        }
    }

    try:
        response = requests.post(
            HUAWEI_IOT_CONFIG['IAM_URL'],
            headers={'Content-Type': 'application/json'},
            data=json.dumps(auth_data),
            timeout=10  # 添加超时
        )
        token = response.headers.get('X-Subject-Token')
        print("获取Token成功")
        with open('token.txt', 'w') as f:
            f.write(token)
            f.close()

        if token:
            _huawei_token = token
            _token_expire = time.time() + 3500  # 设置为3500秒（约58分钟）
            return token
        return None
    except Exception as e:
        print(f"获取Token失败: {e}")
        return None


def check_and_refresh_token():
    global _huawei_token, _token_expire
    if not _huawei_token or time.time() > _token_expire:
        return get_huawei_token()
    return _huawei_token


# def check_and_refresh_token():
#     if 'huawei_token' not in session or time.time() > session.get('token_expire', 0):
#         return get_huawei_token()
#     return session['huawei_token']


# 设置设备属性
def set_device_properties(command):
    """发送设备控制命令（支持完整 command 结构）"""
    token = check_and_refresh_token()
    if not token:
        return False

    url = f"{HUAWEI_IOT_CONFIG['IOT_URL']}/devices/{HUAWEI_IOT_CONFIG['DEVICE_ID']}/commands"

    try:
        payload = json.dumps(command)
        print("发送的 payload:", payload)

        response = requests.post(
            url=url,
            headers={
                'Content-Type': 'application/json',
                'X-Auth-Token': token
            },
            data=payload,
            timeout=10  # 添加超时
        )

        if response.status_code in [200, 204]:
            print("设备控制命令发送成功")
            return True
        else:
            print(f"设备控制失败，状态码：{response.status_code}，响应内容：{response.text}")
            return False
    except Exception as e:
        print(f"设备控制失败: {e}")
        return False


def save_token_to_file(token, file_path="huawei_token.json"):
    with open(file_path, 'w') as f:
        json.dump({'token': token}, f)


def load_token_from_file(file_path="huawei_token.json"):
    if os.path.exists(file_path):
        with open(file_path, 'r') as f:
            data = json.load(f)
            return data.get('token')
    return None


def get_device_shadow():
    token = load_token_from_file()

    if not token:
        token = get_huawei_token()
        if not token:
            return None
        save_token_to_file(token)

    url = f"{HUAWEI_IOT_CONFIG['IOT_URL']}/devices/{HUAWEI_IOT_CONFIG['DEVICE_ID']}/shadow"

    try:
        response = requests.get(
            url,
            headers={
                'Content-Type': 'application/json',
                'X-Auth-Token': token
            }
        )

        if response.status_code == 200:
            data = response.json()
            if data.get('shadow') and data['shadow'][0].get('reported'):
                return data['shadow'][0]['reported']['properties']
        elif response.status_code == 401:  # Token expired or invalid
            token = get_huawei_token()
            if not token:
                return None
            save_token_to_file(token)
            # Retry the request with the new token
            response = requests.get(
                url,
                headers={
                    'Content-Type': 'application/json',
                    'X-Auth-Token': token
                }
            )
            if response.status_code == 200:
                data = response.json()
                print("data['shadow'][0]['reported']['properties']")
                print(data['shadow'][0]['reported']['properties'])
                if data.get('shadow') and data['shadow'][0].get('reported'):
                    return data['shadow'][0]['reported']['properties']
        return None
    except Exception as e:
        print(f"获取设备影子失败: {e}")
        return None


# 通过token来获取数据


import uuid


def show_device_info(device_id=None):
    """显示当前设备信息，支持可选 device_id"""
    if not device_id:
        try:
            if data_queue:
                info = data_queue[-1]
                device_id = info.get('id', '1')  # 默认设备 ID
            else:
                device_id = '1'  # 默认设备 ID
        except IndexError:
            device_id = '1'
    # from app import create_app
    # current_app = create_app()
    from main import app
    current_app = app
    with current_app.app_context():
        device = Device.query.filter_by(id=device_id).first() if device_id else None
        if device:
            latest_shadow = DeviceShadowData.query.filter_by(device_id=device.id).order_by(
                DeviceShadowData.timestamp.desc()).first()
            if latest_shadow:
                return {
                    "device_id": device.id,
                    "server": "iot.huaweicloud.com:1883",  # 硬编码默认值
                    "protocol": "mqtt",
                    "service_id": "mqtt",
                    "stat": latest_shadow.stat or False,
                    "temperature": latest_shadow.tem or 0.0,
                    "humidity": latest_shadow.hum or 0.0,
                    "light": latest_shadow.light or 0.0,
                    "soil": latest_shadow.soil or 0.0,
                    "rain": latest_shadow.rain or 0.0,
                    "uv": latest_shadow.uv or 0,
                    "ph": latest_shadow.co2 or 0,
                    "led": latest_shadow.led or False,
                    "fan": latest_shadow.fan or False,
                    "water": latest_shadow.water or False,
                    "led_status": "开启" if latest_shadow.led else "关闭",
                    "device_status": "在线" if latest_shadow.stat else "离线"
                }
        return {
            "device_id": device_id or None,
            "server": "iot.huaweicloud.com:1883",
            "protocol": "mqtt",
            "service_id": "mqtt",
            "stat": False,
            "temperature": 0.0,
            "humidity": 0.0,
            "light": 0.0,
            "soil": 0.0,
            "rain": 0.0,
            "uv": 0,
            "ph": 0,
            "led": False,
            "fan": False,
            "water": False,
            "led_status": "关闭",
            "device_status": "离线"
        }


THRESHOLDS = {
    'temperature': {'max': 35.0, 'min': 10.0},  # 温度范围：10°C ~ 35°C
    'humidity': {'max': 80.0, 'min': 20.0},  # 湿度范围：20% ~ 80%
    'light': {'max': 1000.0, 'min': 0.0},  # 光照范围：0 ~ 1000
    'soil': {'max': 90.0, 'min': 10.0},  # 土壤湿度：10% ~ 90%
    'rain': {'max': 50.0, 'min': 0.0},  # 降雨量：0 ~ 50
    'uv': {'max': 10.0, 'min': 0.0},  # 紫外线指数：0 ~ 10
    'ph': {'max': 1000, 'min': 0.0},  # pH值：6 ~ 8
}


def update_device_shadow_data(app):
    """更新设备影子数据"""
    data = get_device_shadow()
    if not data:
        log("无设备影子数据可更新")
        return
    props = data
    device_id = props.get('id', 0)
    print(data)
    info = {
        "device_id": props.get('id', 0),
        "server": f"{device_config['hostname']}:{device_config['port']}",
        "protocol": device_config["protocol"],
        "service_id": "mqtt",
        "temperature": props.get("tem", 0.0),
        "humidity": props.get("hum", 0.0),
        "light": props.get("light", 0.0),
        "soil": props.get("soil", 0.0),
        "rain": props.get("rain", 0.0),
        "uv": props.get("uv", 0),
        "ph": props.get("ph", 0),
        "led": props.get("led", False),
        "fan": props.get("fan", False),
        "water": props.get("water", False),
        "led_status": "开启" if props.get("led", False) else "关闭",
        "device_status": "在线" if props.get("stat", False) else "离线"
    }
    data_queue.append(info)
    ai_queue.append(info)

    log(f"更新 data_queue: {info}")

    with app.app_context():
        try:
            device = Device.query.filter_by(id=device_id).first()

            # 如果有超标数据，发送警报邮件
            if not device:
                log(f"未找到设备 {device_id}，创建新设备")
                device = Device(
                    id=device_id,
                    name=props.get("name", f"HUAWEI_DEVICE_{device_id}"),  # 默认名称
                )
                db.session.add(device)
                db.session.commit()
                log(f"已创建新设备: {device_id}")

            shadow_data = DeviceShadowData(
                id=str(uuid.uuid4()),
                device_id=device.id,
                stat=props.get("stat", False),
                hum=props.get("hum", 0.0),
                tem=props.get("tem", 0.0),
                light=props.get("light", 0.0),
                soil=props.get("soil", 0.0),
                rain=props.get("rain", 0.0),
                uv=props.get("uv", 0.0),
                co2=props.get("ph", 0.0),
                led=props.get("led", False),
                fan=props.get("fan", False),
                water=props.get("water", False),
                timestamp=datetime.utcnow()
            )
            db.session.add(shadow_data)
            db.session.commit()
            log(f"设备 {device_id} 的影子数据已存储到数据库")

        except db.exc.SQLAlchemyError as e:
            log(f"数据库操作失败: {e}")
            db.session.rollback()
    # log(f"data_queue 内容: {list(data_queue)}")


def start_shadow_query_interval():
    from main import app
    def shadow_query_task():
        with app.app_context():
            update_device_shadow_data(app)
        # 递归调度下一个任务
        global shadow_query_interval
        shadow_query_interval = Timer(5.0, shadow_query_task)
        shadow_query_interval.start()

    global shadow_query_interval
    # 仅在 shadow_query_interval 未定义或已取消时启动
    if 'shadow_query_interval' not in globals() or not shadow_query_interval.is_alive():
        with app.app_context():
            update_device_shadow_data(app)  # 初始调用
        shadow_query_interval = Timer(5.0, shadow_query_task)
        shadow_query_interval.start()
